import pytest
import asyncio
from typing import AsyncGenerator, Generator

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import NullPool

from app.database import Base, get_db
from app.config import settings
from app.redis_client import redis_client
from app.main import app
from fastapi.testclient import TestClient

# 测试数据库URL
TEST_DATABASE_URL = "postgresql+asyncpg://postgres:postgres@localhost:5432/test_db"

# 创建测试引擎
test_engine = create_async_engine(
    TEST_DATABASE_URL, 
    poolclass=NullPool,
    echo=False
)

# 创建测试会话工厂
test_async_session = sessionmaker(
    test_engine, 
    class_=AsyncSession, 
    expire_on_commit=False,
    autoflush=False
)

@pytest.fixture(scope="session")
def event_loop() -> Generator[asyncio.AbstractEventLoop, None, None]:
    """
    创建事件循环，用于异步测试
    """
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()

@pytest.fixture(scope="session")
async def setup_database() -> AsyncGenerator[None, None]:
    """
    设置测试数据库
    创建所有表，测试完成后删除所有表
    """
    # 创建所有表
    async with test_engine.begin() as conn:
        await conn.run_sync(Base.metadata.drop_all)
        await conn.run_sync(Base.metadata.create_all)
    
    yield
    
    # 删除所有表
    async with test_engine.begin() as conn:
        await conn.run_sync(Base.metadata.drop_all)

@pytest.fixture
async def db_session(setup_database) -> AsyncGenerator[AsyncSession, None]:
    """
    创建测试数据库会话
    """
    async with test_async_session() as session:
        try:
            yield session
        finally:
            await session.close()

@pytest.fixture
async def override_get_db(db_session: AsyncSession) -> AsyncGenerator[AsyncSession, None]:
    """
    覆盖依赖注入函数，使用测试数据库会话
    """
    try:
        yield db_session
    finally:
        pass

@pytest.fixture
def test_client(override_get_db) -> Generator[TestClient, None, None]:
    """
    创建测试客户端
    """
    # 覆盖依赖注入
    app.dependency_overrides[get_db] = lambda: override_get_db
    
    # 创建测试客户端
    with TestClient(app) as client:
        yield client
    
    # 清理
    app.dependency_overrides.clear()

@pytest.fixture
async def mock_redis():
    """
    模拟Redis客户端
    """
    # 保存原始方法
    original_get = redis_client.get
    original_set = redis_client.set
    original_delete = redis_client.delete
    original_exists = redis_client.exists
    original_clear_pattern = redis_client.clear_pattern
    
    # 创建内存缓存
    memory_cache = {}
    
    # 模拟方法
    async def mock_get(key: str):
        return memory_cache.get(key)
    
    async def mock_set(key: str, value, ttl=None):
        memory_cache[key] = value
        return True
    
    async def mock_delete(key: str):
        if key in memory_cache:
            del memory_cache[key]
            return True
        return False
    
    async def mock_exists(key: str):
        return key in memory_cache
    
    async def mock_clear_pattern(pattern: str):
        keys_to_delete = []
        for key in memory_cache:
            if pattern in key:
                keys_to_delete.append(key)
        
        count = 0
        for key in keys_to_delete:
            del memory_cache[key]
            count += 1
        
        return count
    
    # 替换方法
    redis_client.get = mock_get
    redis_client.set = mock_set
    redis_client.delete = mock_delete
    redis_client.exists = mock_exists
    redis_client.clear_pattern = mock_clear_pattern
    
    yield
    
    # 恢复原始方法
    redis_client.get = original_get
    redis_client.set = original_set
    redis_client.delete = original_delete
    redis_client.exists = original_exists
    redis_client.clear_pattern = original_clear_pattern
